package com.core.springSecurity.provider;

import com.common.ErrorCodeEnum;
import com.common.exception.BusinessException;
import com.core.springSecurity.authentication.PasswordAuthenticationToken;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

public class PasswordAuthenticationProvider implements AuthenticationProvider {

    BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();

    private UserDetailsService userDetailsService;

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        String username = authentication.getPrincipal() == null ? "NONE_PROVIDED" : authentication.getName();
        UserDetails user = userDetailsService.loadUserByUsername(username);
        checkUserPassword(user.getPassword(), authentication);


        Object principalToReturn = user;

        return this.createSuccessAuthentication(principalToReturn, authentication, user);
    }

    private void checkUserPassword(String password, Authentication authentication) {
        boolean matches = passwordEncoder.matches(String.valueOf(authentication.getCredentials()), password);
        if (!matches) {
            throw new BusinessException(ErrorCodeEnum.USER_FAILED);
        }
    }

    protected Authentication createSuccessAuthentication(Object principal, Authentication authentication,
                                                         UserDetails user) {
        PasswordAuthenticationToken result = new PasswordAuthenticationToken(principal,
                authentication.getCredentials(), user.getAuthorities());
        result.setDetails(authentication.getDetails());
        SecurityContextHolder.getContext().setAuthentication(result);
        return result;
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return PasswordAuthenticationToken.class.isAssignableFrom(authentication);
    }

    public UserDetailsService getUserDetailsService() {
        return userDetailsService;
    }

    public void setUserDetailsService(UserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
    }
}
